﻿using Messhall.Framework.Dapr.Model;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Messhall.Framework.Dapr
{
    public class DaprStarter
    {
        [DllImport("user32.dll")]
        private static extern int SetWindowText(IntPtr hWnd, string text);

        private const string USER_PROFILE = "%USERPROFILE%";

        private static bool _spinLock = false;

        public static Process Start(DaprConfig config)
        {
            if (!string.IsNullOrWhiteSpace(config.ExtendedParameter)
                && config.ExtendedParameter.Contains(USER_PROFILE))
            {
                config.ExtendedParameter = config.ExtendedParameter.Replace(USER_PROFILE, Environment.GetFolderPath(Environment.SpecialFolder.UserProfile));
            }


            var appId = config.AppId;

            _spinLock = config.CreateNoWindow;
            var daprProcess = new Process();
            daprProcess.StartInfo = new ProcessStartInfo(
                "daprd",
                $"-app-id {appId} -app-port {config.AppPort} -metrics-port {config.MetricsPort} -dapr-grpc-port {config.DaprGrpcPort} -dapr-http-port {config.DaprHttpPort}{config.ExtendedParameter}"
            );
            daprProcess.StartInfo.UseShellExecute = !config.CreateNoWindow;
            daprProcess.StartInfo.CreateNoWindow = config.CreateNoWindow;

            Console.WriteLine($"[DaprStarter] cmd: { daprProcess.StartInfo.Arguments }");

            if (config.CreateNoWindow)
            {
                daprProcess.StartInfo.RedirectStandardInput = true;
                daprProcess.StartInfo.RedirectStandardError = true;
                daprProcess.StartInfo.RedirectStandardOutput = true;

                daprProcess.OutputDataReceived += DaprProcess_OutputDataReceived;
                daprProcess.ErrorDataReceived += DaprProcess_ErrorDataReceived;
            }

            new ProcessManager().KillByPort(config.DaprHttpPort);

            daprProcess.Start();

            if (config.CreateNoWindow)
            {
                daprProcess.BeginOutputReadLine();
                daprProcess.BeginErrorReadLine();
            }

            AppDomain.CurrentDomain.DomainUnload += (s, e) => daprProcess.Kill();

            while (_spinLock)
            {
                Thread.Sleep(500);
            }

            return daprProcess;
        }

        private static void DaprProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data == null) return;

            if (e.Data.Contains("application protocol: http. waiting on port")) _spinLock = false;

            var dataSpan = e.Data.AsSpan();
            var levelStartIndex = e.Data.IndexOf("level=") + 6;
            var levelLength = dataSpan.Slice(levelStartIndex).IndexOf(' ');
            var level = dataSpan.Slice(levelStartIndex, levelLength);

            var color = Console.ForegroundColor;

            switch (level.ToString())
            {
                case "warning":
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    break;
                case "error":
                case "critical":
                case "fatal":
                    Console.ForegroundColor = ConsoleColor.Red;
                    break;
                default:
                    break;
            };

            Console.WriteLine(e.Data);
            Console.ForegroundColor = color;
        }

        private static void DaprProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data == null) return;

            var color = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(e.Data);
            Console.ForegroundColor = color;
        }
    }

    public enum DaprLogLevels
    {
        Debug = 1,

        Verbose,

        Info,

        Warning,

        Error,

        Critical,

        Fatal,
    }

}